/*******************************************************************************
* Copyright (c) 2009, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Simon Scholz <simon.scholz@vogella.com> - Bug 433450
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654
******************************************************************************/
package org.eclipse.ui.internal.e4.compatibility;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.advanced.MArea;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainerElement;
import org.eclipse.e4.ui.model.application.ui.basic.MPartStack;
import org.eclipse.e4.ui.model.application.ui.basic.MStackElement;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IFolderLayout;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPlaceholderFolderLayout;
import org.eclipse.ui.IPluginContribution;
import org.eclipse.ui.IViewLayout;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.activities.IIdentifier;
import org.eclipse.ui.activities.IIdentifierListener;
import org.eclipse.ui.activities.IWorkbenchActivitySupport;
import org.eclipse.ui.activities.IdentifierEvent;
import org.eclipse.ui.activities.WorkbenchActivityHelper;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPage;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.misc.StatusUtil;
import org.eclipse.ui.internal.registry.ActionSetRegistry;
import org.eclipse.ui.internal.registry.IActionSetDescriptor;
import org.eclipse.ui.views.IViewDescriptor;
import org.eclipse.ui.views.IViewRegistry;
public class ModeledPageLayout implements IPageLayout {
public static final String ACTION_SET_TAG = "persp.actionSet:"; //$NON-NLS-1$
public static final String NEW_WIZARD_TAG = "persp.newWizSC:"; //$NON-NLS-1$
public static final String PERSP_SHORTCUT_TAG = "persp.perspSC:"; //$NON-NLS-1$
public static final String SHOW_IN_PART_TAG = "persp.showIn:"; //$NON-NLS-1$
public static final String SHOW_VIEW_TAG = "persp.viewSC:"; //$NON-NLS-1$
public static final String EDITOR_STACK_TAG = "EditorStack"; //$NON-NLS-1$
public static final String HIDDEN_MENU_PREFIX = "persp.hideMenuSC:"; //$NON-NLS-1$
public static final String HIDDEN_TOOLBAR_PREFIX = "persp.hideToolbarSC:"; //$NON-NLS-1$
public static final String HIDDEN_ACTIONSET_PREFIX = "persp.hideActionSetSC:"; //$NON-NLS-1$
public static final String HIDDEN_ITEMS_KEY = "persp.hiddenItems"; //$NON-NLS-1$
public static List<String> getIds(MPerspective model, String tagPrefix) {
if (model == null) {
return Collections.emptyList();
}
ArrayList<String> result = new ArrayList<>();
for (String tag : model.getTags()) {
if (tag.startsWith(tagPrefix)) {
result.add(tag.substring(tagPrefix.length()));
}
}
return result;
}
private MApplication application;
// private MWindow window;
private EModelService modelService;
EPartService partService;
WorkbenchPage page;
MPerspective perspModel;
private IPerspectiveDescriptor descriptor;
private MPlaceholder eaRef;
private MPartStack editorStack;
boolean createReferences;
private IViewRegistry viewRegistry;
private ModeledPageLayoutUtils layoutUtils;
private class ViewActivator implements IIdentifierListener {
private MUIElement element;
public ViewActivator(MUIElement element) {
this.element = element;
}
@Override
public void identifierChanged(IdentifierEvent identifierEvent) {
IIdentifier identifier = identifierEvent.getIdentifier();
// Not activated, do nothing
if (!identifier.isEnabled())
return;
// stop listening for activations
identifier.removeIdentifierListener(this);
element.setToBeRendered(true);
}
}
public ModeledPageLayout(MWindow window, EModelService modelService,
EPartService partService,
MPerspective perspModel, IPerspectiveDescriptor descriptor, WorkbenchPage page,
boolean createReferences) {
// this.window = window;
MUIElement winParent = window.getParent();
this.application = (MApplication) winParent;
this.modelService = modelService;
this.partService = partService;
this.viewRegistry = PlatformUI.getWorkbench().getViewRegistry();
this.page = page;
// Create the editor area stack
this.perspModel = perspModel;
this.descriptor = descriptor;
this.layoutUtils = new ModeledPageLayoutUtils(modelService);
this.createReferences = createReferences;
MArea sharedArea = null;
List<MUIElement> sharedElements = window.getSharedElements();
for (MUIElement element : sharedElements) {
if (element.getElementId().equals(getEditorArea())) {
sharedArea = (MArea) element;
break;
}
}
if (sharedArea == null) {
sharedArea = modelService.createModelElement(MArea.class);
// sharedArea.setLabel("Editor Area"); //$NON-NLS-1$
editorStack = modelService.createModelElement(MPartStack.class);
editorStack.getTags().add("org.eclipse.e4.primaryDataStack"); //$NON-NLS-1$
editorStack.getTags().add(EDITOR_STACK_TAG);
editorStack.setElementId("org.eclipse.e4.primaryDataStack"); //$NON-NLS-1$
sharedArea.getChildren().add(editorStack);
sharedArea.setElementId(getEditorArea());
window.getSharedElements().add(sharedArea);
} else {
List<MPartStack> stacks = modelService.findElements(sharedArea, null, MPartStack.class,
null);
if (!stacks.isEmpty()) {
editorStack = stacks.get(0);
}
}
eaRef = modelService.createModelElement(MPlaceholder.class);
eaRef.setElementId(getEditorArea());
eaRef.setRef(sharedArea);
perspModel.getChildren().add(eaRef);
ActionSetRegistry registry = application.getContext().get(ActionSetRegistry.class);
for (IActionSetDescriptor actionSetDescriptor : registry.getActionSets()) {
if (actionSetDescriptor.isInitiallyVisible()) {
addActionSet(actionSetDescriptor.getId());
}
}
}
public MPerspective getModel() {
return perspModel;
}
@Override
public void addActionSet(String actionSetId) {
perspModel.getTags().add(ACTION_SET_TAG + actionSetId);
}
@Override
public void addFastView(String viewId) {
E4Util.unsupported("addFastView: " + viewId); //$NON-NLS-1$
logDeprecatedWarning(viewId);
}
@Override
public void addFastView(String viewId, float ratio) {
E4Util.unsupported("addFastView: " + viewId); //$NON-NLS-1$
logDeprecatedWarning(viewId);
}
private void logDeprecatedWarning(String viewId) {
String message = viewId + ": Deprecated relationship \"fast\" should be converted to \"stack\"."; //$NON-NLS-1$
WorkbenchPlugin.log(message, StatusUtil.newStatus(IStatus.WARNING, message, null));
}
@Override
public void addNewWizardShortcut(String id) {
perspModel.getTags().add(NEW_WIZARD_TAG + id);
}
@Override
public void addPerspectiveShortcut(String id) {
perspModel.getTags().add(PERSP_SHORTCUT_TAG + id);
}
@Override
public void addPlaceholder(String viewId, int relationship, float ratio,
String refId) {
insertView(viewId, relationship, ratio, refId, false, true);
}
@Override
public void addShowInPart(String id) {
perspModel.getTags().add(SHOW_IN_PART_TAG + id);
}
@Override
public void addShowViewShortcut(String id) {
perspModel.getTags().add(SHOW_VIEW_TAG + id);
}
@Override
public void addStandaloneView(String viewId, boolean showTitle,
int relationship, float ratio, String refId) {
MUIElement newElement = insertView(viewId, relationship, ratio, refId, true, showTitle);
if (newElement instanceof MPartStack) {
MPartStack stack = (MPartStack) newElement;
stack.getTags().add(IPresentationEngine.STANDALONE);
stack.getChildren().get(0).getTags().add(IPresentationEngine.NO_MOVE);
} else {
newElement.getTags().add(IPresentationEngine.STANDALONE);
}
}
@Override
public void addStandaloneViewPlaceholder(String viewId, int relationship,
float ratio, String refId, boolean showTitle) {
MUIElement newElement = insertView(viewId, relationship, ratio, refId, false, showTitle);
if (newElement instanceof MPartStack) {
MPartStack stack = (MPartStack) newElement;
stack.getTags().add(IPresentationEngine.STANDALONE);
stack.getChildren().get(0).getTags().add(IPresentationEngine.NO_MOVE);
} else {
newElement.getTags().add(IPresentationEngine.STANDALONE);
}
}
@Override
public void addView(String viewId, int relationship, float ratio, String refId) {
insertView(viewId, relationship, ratio, refId, true, true);
}
public void addView(String viewId, int relationship, float ratio, String refId,
boolean minimized) {
if (minimized) {
E4Util.unsupported("addView: use of minimized for " + viewId + " ref " + refId); //$NON-NLS-1$ //$NON-NLS-2$
}
addView(viewId, relationship, ratio, refId);
}
protected boolean isViewFiltered(String viewID) {
IViewDescriptor viewDescriptor = viewRegistry.find(viewID);
if (viewDescriptor == null)
return false;
if (WorkbenchActivityHelper.restrictUseOf(viewDescriptor))
return true;
return WorkbenchActivityHelper.filterItem(viewDescriptor);
}
protected void addViewActivator(MUIElement element) {
IPluginContribution contribution = (IPluginContribution) viewRegistry.find(element
.getElementId());
IWorkbenchActivitySupport support = PlatformUI.getWorkbench().getActivitySupport();
IIdentifier identifier = support.getActivityManager().getIdentifier(
WorkbenchActivityHelper.createUnifiedId(contribution));
identifier.addIdentifierListener(new ViewActivator(element));
}
@Override
public IFolderLayout createFolder(String folderId, int relationship,
float ratio, String refId) {
MPartStack stack = insertStack(folderId, relationship, ratio, refId,
false);
return new ModeledFolderLayout(this, application, stack);
}
@Override
public IPlaceholderFolderLayout createPlaceholderFolder(String folderId,
int relationship, float ratio, String refId) {
MPartStack Stack = insertStack(folderId, relationship, ratio, refId,
false);
return new ModeledPlaceholderFolderLayout(this, application, Stack);
}
@Override
public IPerspectiveDescriptor getDescriptor() {
return descriptor;
}
public static String internalGetEditorArea() {
return IPageLayout.ID_EDITOR_AREA;
}
@Override
public String getEditorArea() {
return internalGetEditorArea();
}
@Override
public int getEditorReuseThreshold() {
return -1;
}
@Override
public IPlaceholderFolderLayout getFolderForView(String id) {
MPart view = findPart(perspModel, id);
if (view == null)
return null;
MUIElement stack = view.getParent();
if (stack == null || !(stack instanceof MPartStack))
return null;
return new ModeledPlaceholderFolderLayout(this, application, (MPartStack) stack);
}
@Override
public IViewLayout getViewLayout(String id) {
MPart view = findPart(perspModel, id);
if (view != null)
return new ModeledViewLayout(view);
MPlaceholder placeholder = findPlaceholder(perspModel, id);
if (placeholder != null)
return new ModeledViewLayout(placeholder);
return null;
}
@Override
public boolean isEditorAreaVisible() {
return true;
}
@Override
public boolean isFixed() {
return false;
}
@Override
public void setEditorAreaVisible(boolean showEditorArea) {
eaRef.setToBeRendered(showEditorArea);
}
@Override
public void setEditorReuseThreshold(int openEditors) {
// ignored, no-op, same as 3.x implementation
}
@Override
public void setFixed(boolean isFixed) {
// perspModel.setFixed(isFixed);
}
public static MStackElement createViewModel(MApplication application, String id,
boolean visible,
WorkbenchPage page, EPartService partService, boolean createReferences) {
EModelService ms = application.getContext().get(EModelService.class);
MPartDescriptor partDesc = ms.getPartDescriptor(id);
if (partDesc != null) {
MPlaceholder ph = partService.createSharedPart(id);
ph.setToBeRendered(visible);
MPart part = (MPart) (ph.getRef());
// as a shared part, this should be true, actual un/rendering
// will be dependent on any placeholders that are referencing
// this part
part.setToBeRendered(true);
// there should only be view references for 3.x views that are
// visible to the end user, that is, the tab items are being
// drawn
if (visible
&& createReferences
&& CompatibilityPart.COMPATIBILITY_VIEW_URI.equals(partDesc
.getContributionURI())) {
page.createViewReferenceForPart(part, id);
}
return ph;
}
return null;
}
private MUIElement insertView(String viewId, int relationship, float ratio,
String refId, boolean visible, boolean withStack) {
// Hide views that are filtered by capabilities
boolean isFiltered = isViewFiltered(viewId);
MStackElement viewModel = createViewModel(application, viewId, visible && !isFiltered,
page, partService,
createReferences);
MUIElement retVal = viewModel;
if (viewModel != null) {
if (withStack) {
String stackId = viewId + "MStack"; // Default id...basically unusable //$NON-NLS-1$
MPartStack stack = insertStack(stackId, relationship, ratio, refId, visible
& !isFiltered);
stack.getChildren().add(viewModel);
retVal = stack;
} else {
layoutUtils.insert(viewModel, findRefModel(refId), layoutUtils.plRelToSwt(relationship), ratio);
}
}
if (isFiltered) {
addViewActivator(viewModel);
}
return retVal;
}
private MUIElement findRefModel(String refId) {
MUIElement refModel = findElement(perspModel, refId);
if (refModel instanceof MPart) {
MUIElement parent = refModel.getParent();
return parent instanceof MPartStack ? parent : refModel;
} else if (refModel instanceof MPlaceholder) {
MUIElement ref = ((MPlaceholder) refModel).getRef();
if (ref instanceof MPart) {
MUIElement parent = refModel.getParent();
return parent instanceof MPartStack ? parent : refModel;
}
}
return refModel;
}
private MUIElement getLastElement(MUIElement element) {
if (element instanceof MElementContainer<?>) {
MElementContainer<?> container = (MElementContainer<?>) element;
List<?> children = container.getChildren();
return children.isEmpty() ? container : getLastElement((MUIElement) children
.get(children.size() - 1));
}
MUIElement parent = element.getParent();
return parent == perspModel ? element : parent;
}
/**
* Returns the element that is the deepest and last element of the
* containers underneath the current perspective. If this element's parent
* is the perspective itself, the element will be returned. The perspective
* will only be returned if the perspective itself has no children.
*
* @return the parent of the final element in the recursion chain of
* children, or the element itself if its parent is the perspective,
* or the perspective if the perspective itself has no children
*/
private MUIElement getLastElement() {
List<MPartSashContainerElement> children = perspModel.getChildren();
if (children.isEmpty()) {
return perspModel;
}
return getLastElement(children.get(children.size() - 1));
}
private MPartStack insertStack(String stackId, int relationship,
float ratio, String refId, boolean visible) {
MUIElement refModel = findElement(perspModel, refId);
if (refModel == null) {
WorkbenchPlugin.log(NLS.bind(WorkbenchMessages.PageLayout_missingRefPart, refId));
MPartStack stack = layoutUtils.createStack(stackId, visible);
layoutUtils.insert(stack, getLastElement(), layoutUtils.plRelToSwt(relationship), ratio);
return stack;
}
// If the 'refModel' is -not- a stack then find one
// This covers cases where the defining layout is adding
// Views relative to other views and relying on the stacks
// being automatically created.
// if (!(refModel instanceof MPartStack)) {
// while (refModel.getParent() != null) {
// refModel = refModel.getParent();
// if (refModel instanceof MPartStack)
// break;
// }
// if (!(refModel instanceof MPartStack))
// return null;
// }
MPartStack stack = layoutUtils.createStack(stackId, visible);
MElementContainer<?> parent = refModel.getParent();
if (parent instanceof MPartStack) {
// we don't want to put a stack in a stack
refModel = parent;
}
layoutUtils.insert(stack, refModel, layoutUtils.plRelToSwt(relationship), ratio);
return stack;
}
public static void replace(MUIElement relTo,
MElementContainer<MUIElement> newParent) {
if (relTo == null || newParent == null)
return;
MElementContainer<MUIElement> parent = relTo.getParent();
if (parent == null)
return;
List<MUIElement> kids = parent.getChildren();
if (kids == null)
return;
kids.add(kids.indexOf(relTo), newParent);
kids.remove(relTo);
}
public static void insertParent(MElementContainer<MUIElement> newParent,
MUIElement relTo) {
if (newParent == null || relTo == null)
return;
MPart curParent = (MPart) relTo.getParent();
if (curParent != null) {
replace(relTo, newParent);
}
// Move the child under the new parent
newParent.getChildren().add(relTo);
}
MUIElement findElement(MUIElement toSearch, String id) {
List<Object> found = modelService.findElements(toSearch, id, null, null,
EModelService.IN_ANY_PERSPECTIVE);
if (found.size() > 0)
return (MUIElement) found.get(0);
MUIElement foundElement = modelService.find(id, toSearch);
return foundElement;
}
private MPart findPart(MUIElement toSearch, String id) {
MUIElement element = modelService.find(id, toSearch);
return element instanceof MPart ? (MPart) element : null;
}
private MPlaceholder findPlaceholder(MUIElement toSearch, String id) {
MUIElement element = modelService.find(id, toSearch);
return element instanceof MPlaceholder ? (MPlaceholder) element : null;
}
public void addHiddenMenuItemId(String id) {
page.addHiddenItems(perspModel, HIDDEN_MENU_PREFIX + id);
}
public void addHiddenToolBarItemId(String id) {
page.addHiddenItems(perspModel, HIDDEN_TOOLBAR_PREFIX + id);
}
public void removePlaceholder(String id) {
MUIElement refModel = findElement(perspModel, id);
if (!(refModel instanceof MPlaceholder)) {
E4Util.unsupported("removePlaceholder: failed to find " + id + ": " + refModel); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
// placeholders in the shared area should be ignored
if (modelService.getElementLocation(refModel) != EModelService.IN_SHARED_AREA) {
MElementContainer<MUIElement> parent = refModel.getParent();
if (parent != null) {
parent.getChildren().remove(refModel);
}
}
}
public void stackView(String id, String refId, boolean visible) {
MUIElement refModel = refId.equals(getEditorArea()) ? editorStack : findElement(perspModel,
refId);
if (refModel == null) {
addView(id, LEFT, DEFAULT_VIEW_RATIO, refId);
return;
}
if (refModel instanceof MPart || refModel instanceof MPlaceholder) {
refModel = refModel.getParent();
}
if (!(refModel instanceof MPartStack)) {
E4Util.unsupported("stackView: failed to find " + refId + " for " + id); //$NON-NLS-1$//$NON-NLS-2$
return;
}
// Hide views that are filtered by capabilities
boolean isFiltered = isViewFiltered(id);
boolean toBeRendered = visible && !isFiltered;
MStackElement viewModel = createViewModel(application, id, toBeRendered, page, partService,
createReferences);
if (viewModel != null) {
MPartStack stack = (MPartStack) refModel;
boolean wasEmpty = stack.getChildren().isEmpty();
stack.getChildren().add(viewModel);
if (wasEmpty && toBeRendered) {
// the stack didn't originally have any children, set this as
// the selected element
stack.setSelectedElement(viewModel);
}
if (viewModel.isToBeRendered()) {
// ensure that the parent is being rendered, it may have been a
// placeholder folder so its flag may actually be false
layoutUtils.resetToBeRenderedFlag(viewModel, true);
}
if (isFiltered) {
addViewActivator(viewModel);
}
}
}
}